package com.example.spring.jpa.utils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author: orange
 * @create: 2023-07-25 15:54
 * @description:
 */
public class ClassUtils {

    private final static List<String> basicDataType = new ArrayList<>();
    private final Map<String, Integer> stackCount = new HashMap<>();

    static {
        basicDataType.add("java.lang.Integer");
        basicDataType.add("java.lang.Double");
        basicDataType.add("java.lang.Float");
        basicDataType.add("java.lang.Long");
        basicDataType.add("java.lang.Short");
        basicDataType.add("java.lang.Byte");
        basicDataType.add("java.lang.Boolean");
        basicDataType.add("java.lang.Character");
        basicDataType.add("java.lang.String");
        basicDataType.add("int");
        basicDataType.add("double");
        basicDataType.add("long");
        basicDataType.add("short");
        basicDataType.add("byte");
        basicDataType.add("boolean");
        basicDataType.add("char");
        basicDataType.add("float");
        basicDataType.add("this$0");
        basicDataType.add("java.util.*");
    }

    // 获取类，父类，字段父类、字段本身的字段。
    public Map<String, Object> allFieldAndFieldSuper(Class<?> oClass) {
        // 相互套娃的直接忽略
        if (oClass == null || stackCount.getOrDefault(oClass.getTypeName(), 0) == 1) return null;

        stackCount.put(oClass.getTypeName(),
                stackCount.getOrDefault(oClass.getTypeName(), 0) + 1);

        // 所有字段，包括字段的父类。
        Class<?> currClass = oClass;
        Map<String, Object> objectMap = new HashMap<>();
        while (currClass != null) {
            Field[] fields = currClass.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                if (basicDataType
                        .stream()
                        .anyMatch((e) -> matchPackage(field.getType().getName(), e))) {
                    objectMap.put(field.getName(), field);
                } else {
                    objectMap.put(field.getName(), allFieldAndFieldSuper(field.getType()));
                }
            }
            currClass = currClass.getSuperclass();
        }
        return objectMap;
    }

    public boolean matchPackage(String target, String source) {
        int tl = target.length();
        int sl = source.length();
        boolean[][] dp = new boolean[tl + 1][sl + 1];

        dp[0][0] = true;

        for (int i = 1; i <= tl; i++) {
            for (int j = 1; j <= sl; j++) {
                if (target.charAt(i - 1) == source.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1];
                } else if (source.charAt(j - 1) == '*') {
                    // *有参与匹配和不参与匹配两种情况
                    dp[i][j] = dp[i - 1][j] || dp[i][j - 1];
                }
            }
        }
        return dp[tl][sl];
    }

    // 获取类所有字段，不包括字段里面的字段。
    public static Map<String, Field> classAllField(Class<?> aClass) {
        Map<String, Field> fieldMap = new HashMap<>();
        Class<?> currClass = aClass;
        while (currClass != null) {
            Field[] fields = currClass.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                fieldMap.put(field.getName(), field);
            }
            currClass = currClass.getSuperclass();
        }
        return fieldMap;
    }

    public static void copyProperties(Object source, Object target, PropertiesMerge propertiesMerge) throws IllegalAccessException {
        Map<String, Field> sourceFieldMap = classAllField(source.getClass());
        Map<String, Field> targetFieldMap = classAllField(target.getClass());
        for (String fieldName : targetFieldMap.keySet()) {
            if (sourceFieldMap.containsKey(fieldName)) {
                Field sf = sourceFieldMap.get(fieldName);
                Field tf = targetFieldMap.get(fieldName);
                Object obj = sf.get(source);
                propertiesMerge.merge(target, tf, obj);
            }
        }
    }
}
